<template>
  <div id="map" class="map">
    <div class="tool-box">
      <el-button type="primary" size="small" @click="play">{{playing ? "停止播放" : "播放"}}</el-button>
    </div>
  </div>
</template>
<script>
import "ol/ol.css";
import Map from "ol/Map";
import View from "ol/View";
import layerTile from "ol/layer/Tile";
import OSM from "ol/source/OSM";
import LineString from "ol/geom/LineString";
import VectorSource from "ol/source/Vector";
import Vector from "ol/layer/Vector";
import Feature from "ol/Feature";
import { boundingExtent } from "ol/extent";
import { fromLonLat, toLonLat } from "ol/proj";
import Point from "ol/geom/Point";
import { Icon, Style, Stroke, Fill, Text } from "ol/style";
import white_point_icon from "./images/white_point_icon.png";
import text_blue from "./images/text_blue.png";
import { lineListData } from "./lineData.js";

export default {
  name: "drawArrowPlayRoute",
  data() {
    return {
      map: null,
      roadLineLayer: null,  // 轨迹图层
      playing: false,
      interval: null,
      vectorPlayLayer: null,
      targetList: [],
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.initMap();
    });
  },
  methods: {
    // 初始化
    initMap() {
      this.map = new Map({
        target: "map",
        layers: [
          new layerTile({
            source: new OSM(),
          }),
        ],
        view: new View({
          projection: "EPSG:4326",
          center: [Number(116.3672), Number(23.5772)],
          zoom: 15,
        }),
      });
      // 绘制路径
      this.drawLine(lineListData);
      // 添加轨迹播放图层
      this.addLayserFun();
    },
    // ===============轨迹播放相关逻辑===============开始
    // 添加轨迹播放图层
    addLayserFun() {
      // 轨迹绘制过程的效果
      this.vectorPlayLayer = new Vector({    
        layerName: "动态轨迹播放图层",
        zIndex: 10,    
        source: new VectorSource(),
        style: this.styleFun,
      });
      this.map.addLayer(this.vectorPlayLayer);
    },
    // 点击播放、暂停按钮事件
    play() {
      if(this.playing){
        this.stopPlayTrack()
      } else {
        this.playTrack(lineListData);
      }
    },
    // 轨迹开始播放
    playTrack(arr) {
      this.targetList = arr;
      if (!this.targetList.length) {
        console.log("请至少勾选2条数据，才可以进行轨迹播放！");
        return;
      }
      this.playing = !this.playing;
      // clear图层数据
      this.vectorPlayLayer && this.vectorPlayLayer.getSource().clear();
      this.roadLineLayer && this.roadLineLayer.getSource().clear();
      let _libraryList = JSON.parse(JSON.stringify(this.targetList));
      const pointList = _libraryList.map((item) => {return [item.longitude, item.latitude]});
      this.viewExtent(pointList); // 视野自适应
      this.map.getView().setCenter(pointList[0]);
      this.move(0);
    },

    move(idxMove) {
      let _libraryList = JSON.parse(JSON.stringify(this.targetList));
      let playTrackList = JSON.parse(JSON.stringify(this.targetList));
      let zoom = Math.round(this.map.getView().getZoom());
      if (zoom < 8) zoom = 8;
      const pointList = _libraryList.map((item) => {return [item.longitude, item.latitude]});
      
      // 递归move到最后一个
      if (idxMove === pointList.length - 1) {
        this.playing = false;
        setTimeout(() => {
          this.vectorPlayLayer && this.vectorPlayLayer.getSource().clear();
          this.roadLineLayer && this.roadLineLayer.getSource().clear();
          this.drawTrack(playTrackList);
        }, 1000); // 动画结束后调用 drawTrack 方法重新绘制轨迹
        return;
      }
      
      let initPos = pointList[idxMove];
      let targetPos = pointList[idxMove + 1];
      initPos = fromLonLat(initPos);
      targetPos = fromLonLat(targetPos);

      const minZoomLevel = 8; // 最小层级
      const minZLMaxSpeed = 80000;  // 最小层级的移动速度
      const speed = minZLMaxSpeed / Math.pow(2, zoom - minZoomLevel); // 得到对应层级的移动速度

      // 步长，米/秒
      let timer = 100;
      let step = speed / (1000 / timer);
      let currentCount = 0;
      // 总的步长
      let count = Math.round(this.getDistance(initPos, targetPos) / step);
      let tempp;

      let vecsouce = this.vectorPlayLayer.getSource();
      let ll = new Feature({
        geometry: new LineString([toLonLat(initPos), toLonLat(targetPos)]),
      });
      if (count < 1) {
        timer = 10;
      }
      this.interval = setInterval(() => {
        if (count < 1 || currentCount >= count) {
          currentCount = 0;
          vecsouce.addFeature(ll);
          if (count < 1) {
            let m = toLonLat(targetPos);
            this.map.getView().setCenter(m);
          }
          clearInterval(this.interval);
          this.move(++idxMove);
        } else {
          currentCount++;
          if (currentCount < count) {
            let x = null;
            let y = null;
            if (currentCount + 1 >= count) {
              x = targetPos[0];
              y = targetPos[1];
            } else {
              x = this.effect(initPos[0], targetPos[0], currentCount, count);
              y = this.effect(initPos[1], targetPos[1], currentCount, count);
            }
            // 画线
            if (currentCount > 1) {
              let m = toLonLat(tempp);
              let n = toLonLat([x, y]);
              let trace = Math.atan2(x - tempp[0], y - tempp[1]); // Math.atan2(y,x)：返回从原点(0,0)到(x,y)点的线段与x轴正方向之间的平面角度(弧度值)
              let line;
              this.map.getView().setCenter(m);
              if (currentCount % 5 === 0) {
                line = new Feature({
                  geometry: new LineString([m, n]),
                  arrow: true,
                  rotation: trace,
                });
              } else {
                line = new Feature({
                  geometry: new LineString([m, n]),
                  arrow: false,
                });
              }
              vecsouce.addFeature(line);
            }
            if (!tempp) {
              tempp = initPos;
            } else {
              tempp = [x, y];
            }
          }
        }
      }, timer);
    },
    // 重新addFeature绘制轨迹
    drawTrack(arr) {
      let _arr = JSON.parse(JSON.stringify(arr));
      let pointList = [];
      _arr.forEach((item) => {pointList.push([item.longitude, item.latitude])});
      // 视野自适应
      this.viewExtent(pointList);
      for (let i = 0; i < pointList.length - 1; i++) {
        const initPos = pointList[i];
        const targetPos = pointList[i + 1];
        const coordinates = [initPos, targetPos];
        const lineFeature = new Feature({
          geometry: new LineString(coordinates),
        });
        this.roadLineLayer.getSource().addFeature(lineFeature);
      }
    },
    // 轨迹移的图标样式
    styleFun(feature) {
      let styles = [];
      styles.push(
        new Style({
          stroke: new Stroke({
            color: "#10a2ff",
            width: 8,
          }),
        })
      );
      const arrow = feature.get("arrow");
      if (arrow) {
        const rotation = feature.get("rotation");
        const _coords = feature.get("geometry").getCoordinates();
        const point = new Point(_coords[0]);
        styles.push(
          new Style({
            geometry: point,
            image: new Icon({
              src: require("./images/up.png"),
              anchor: [0.5, 0.5],
              rotateWithView: true,
              rotation,
            }),
          })
        );
      }
      return styles;
    },
    // 轨迹暂停播放
    stopPlayTrack() {
      clearInterval(this.interval);
      this.playing = !this.playing;
    },
    // ===============轨迹播放相关逻辑===============结束

    
    // ===============轨迹显示相关逻辑===============开始
    // 绘制路径
    drawLine(lineData) {
      const pointsList = [];
      lineData.forEach((value) => {
          pointsList.push([value.longitude, value.latitude]);
      });
    
      // 绘制带有箭头效果的轨迹
      this.roadLineLayer = new Vector({
        layerName: "路径轨迹图层",
        zIndex: 90,
        source: new VectorSource(),
        style: this.styleArrowFunction,
      });
      this.map.addLayer(this.roadLineLayer);
      for (let i = 0; i < lineData.length - 1; i++) {
        const initPos = pointsList[i];
        const targetPos = pointsList[i + 1];
        const coordinates = [initPos, targetPos];
        const lineFeature = new Feature({
          geometry: new LineString(coordinates),
        });
        this.roadLineLayer.getSource().addFeature(lineFeature);
      }

      this.drawLoc();
    },
    // 绘制位置标示
    drawLoc(opt = {}) {
      if (lineListData.length == 0) {
        return false;
      }
      let pointsList = [];
      let onlyPointList = []
      lineListData.forEach((value) => {
        onlyPointList.push([value.longitude, value.latitude])
        pointsList.push({
            data: value,
            pointLoc: [value.longitude, value.latitude],
        });
      });
      const vectorSource = new VectorSource({ features: [new Feature([])] }); // 矢量标注的数据源
      var features = new Array(pointsList.length);
      for (let idx = 0; idx < pointsList.length; idx++) {
        features[idx] = new Feature({
          // 新建一个要素 ol.Feature
          geometry: new Point(pointsList[idx]["pointLoc"] || []),
          data: pointsList[idx],
        });
        // 添加文字 设置要素的样式
        let obj = {
          imageSrc: opt.imageSrc || white_point_icon,
          text: pointsList[idx]["data"]["indexTxt"],
          opacity: 0.9,
          zIndex: 20,
          fill: "#111",
        };
        features[idx].setStyle(this.createStyleFun(obj));
      }
      vectorSource.addFeatures(features); // 将新要素添加到数据源中
      this.vectorLayer = new Vector({
        layerName: "位置标注图层",
        zIndex: 100,
        source: vectorSource
      }); // 矢量标注图层
      this.map.addLayer(this.vectorLayer);
      this.viewExtent(onlyPointList);   // 视野自适应
    },
    // 视野自适应
    viewExtent(coordinates) {
      this.extent = new boundingExtent(coordinates);
      if (this.map) {
        this.map.getView().fit(this.extent, this.map.getSize());
        this.map.getView().setZoom(this.map.getView().getZoom() - 0.1);
      }
    },
    // 路径轨迹样式
    styleArrowFunction(feature) {
      let zoom = Math.round(this.map.getView().getZoom());
      if (zoom < 8) zoom = 8;
      let styles = [];
      styles.push(
        new Style({
          zIndex: 30,
          stroke: new Stroke({
            color: "#10a2ff",
            width: 8,
          }),
        })
      );
      const _coords = feature.get("geometry").getCoordinates();
      const initPos = _coords[0];
      const targetPos = _coords[1];
      const minZoomLevel = 8; // 最小等级，8
      const minZLMaxPer = 200;

      const per = minZLMaxPer / Math.pow(2, zoom - minZoomLevel); // 1000单位经纬距离箭头数量；箭头间隔随地图层级变化
      let count = Math.round((this.getDistance(initPos, targetPos) / per) * 1000); // 箭头数量 count
      let points = [];
      let angleArr = [];
      let lastPoint = initPos;
      for (let i = 1; i < count; i++) {
        const x = this.effect(initPos[0], targetPos[0], i, count);
        const y = this.effect(initPos[1], targetPos[1], i, count);
        const angle = Math.atan2(x - lastPoint[0], y - lastPoint[1]);
        points.push([x, y]);
        angleArr.push(angle);
        lastPoint = [x, y];
      }
      for (let i = 0; i < points.length; i++) {
        const point = new Point(points[i]);
        styles.push(
          new Style({
            geometry: point,
            zIndex: 100,
            image: new Icon({
              src: require("./images/up.png"),
              anchor: [0.5, 0.5],
              rotateWithView: true,
              rotation: angleArr[i],
            }),
          })
        );
      }
      return styles;
    },
    // 获取2点之间的距离
    getDistance(pxA, pxB) {
      return Math.sqrt(Math.pow(pxA[0] - pxB[0], 2) + Math.pow(pxA[1] - pxB[1], 2));
    },
    // ?不知道是不是用来拆分线段
    effect(initPos, targetPos, currentCount, count) {
      let b = initPos;
      let c = targetPos - initPos;
      let t = currentCount;
      let d = count;
      return (c * t) / d + b;
    },
    // 样式
    createStyleFun(opt = {}) {
      let styleObj = new Style({
        image: new Icon({
          opacity: opt.opacity || 0.75,
          anchor: [0.5, 0.9],
          src: opt.imageSrc || white_point_icon, // text_blue、white_point_icon
        }),
        text: new Text({
          textAlign: "center", // 位置
          font: "14px Microsoft YaHei UI",
          offsetX: 1,
          offsetY: -16,
          text: `${opt.text}`, // 文本内容
          fill: new Fill({ color: opt.fill || "#111" }), // 文本填充样式（即文字颜色）
          stroke: opt.stroke == undefined ? "" : new Stroke({ color: opt.stroke || "#111", width: 14 }),
        }),
        zIndex: opt.zIndex || 10,
      });
      return styleObj;
    },
    // ===============轨迹显示相关逻辑===============结束
  },
};
</script>

<style lang="less" scoped>
.map {
  height: 100%;
  position: relative;
}
.tool-box {
  position: absolute;
  right: 10px;
  top: 10px;
  z-index: 100;
  background: #fff;
  padding: 10px;
  // border: 1px solid #ccc;
  border-radius: 10px;
}
</style>
